home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 10868 / 10868.xpi / modules / log4moz.js < prev    next >
Text File  |  2010-02-02  |  15KB  |  566 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is log4moz
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Michael Johnston
  18.  * Portions created by the Initial Developer are Copyright (C) 2006
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  * Michael Johnston <special.michael@gmail.com>
  23.  * Dan Mills <thunder@mozilla.com>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the terms of
  26.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  27.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28.  * in which case the provisions of the GPL or the LGPL are applicable instead
  29.  * of those above. If you wish to allow use of your version of this file only
  30.  * under the terms of either the GPL or the LGPL, and not to allow others to
  31.  * use your version of this file under the terms of the MPL, indicate your
  32.  * decision by deleting the provisions above and replace them with the notice
  33.  * and other provisions required by the GPL or the LGPL. If you do not delete
  34.  * the provisions above, a recipient may use your version of this file under
  35.  * the terms of any one of the MPL, the GPL or the LGPL.
  36.  *
  37.  * ***** END LICENSE BLOCK ***** */
  38.  
  39. const EXPORTED_SYMBOLS = ['Log4Moz'];
  40.  
  41. const Cc = Components.classes;
  42. const Ci = Components.interfaces;
  43. const Cr = Components.results;
  44. const Cu = Components.utils;
  45.  
  46. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  47.  
  48. const MODE_RDONLY   = 0x01;
  49. const MODE_WRONLY   = 0x02;
  50. const MODE_CREATE   = 0x08;
  51. const MODE_APPEND   = 0x10;
  52. const MODE_TRUNCATE = 0x20;
  53.  
  54. const PERMS_FILE      = 0644;
  55. const PERMS_DIRECTORY = 0755;
  56.  
  57. const ONE_BYTE = 1;
  58. const ONE_KILOBYTE = 1024 * ONE_BYTE;
  59. const ONE_MEGABYTE = 1024 * ONE_KILOBYTE;
  60.  
  61. let Log4Moz = {
  62.   Level: {
  63.     Fatal:  70,
  64.     Error:  60,
  65.     Warn:   50,
  66.     Info:   40,
  67.     Config: 30,
  68.     Debug:  20,
  69.     Trace:  10,
  70.     All:    0,
  71.     Desc: {
  72.       70: "FATAL",
  73.       60: "ERROR",
  74.       50: "WARN",
  75.       40: "INFO",
  76.       30: "CONFIG",
  77.       20: "DEBUG",
  78.       10: "TRACE",
  79.       0:  "ALL"
  80.     }
  81.   },
  82.  
  83.   get repository() {
  84.     delete Log4Moz.repository;
  85.     Log4Moz.repository = new LoggerRepository();
  86.     return Log4Moz.repository;
  87.   },
  88.   set repository(value) {
  89.     delete Log4Moz.repository;
  90.     Log4Moz.repository = value;
  91.   },
  92.  
  93.   get LogMessage() { return LogMessage; },
  94.   get Logger() { return Logger; },
  95.   get LoggerRepository() { return LoggerRepository; },
  96.  
  97.   get Formatter() { return Formatter; },
  98.   get BasicFormatter() { return BasicFormatter; },
  99.  
  100.   get Appender() { return Appender; },
  101.   get DumpAppender() { return DumpAppender; },
  102.   get ConsoleAppender() { return ConsoleAppender; },
  103.   get FileAppender() { return FileAppender; },
  104.   get RotatingFileAppender() { return RotatingFileAppender; },
  105.  
  106.   // Logging helper:
  107.   // let logger = Log4Moz.repository.getLogger("foo");
  108.   // logger.info(Log4Moz.enumerateInterfaces(someObject).join(","));
  109.   enumerateInterfaces: function Log4Moz_enumerateInterfaces(aObject) {
  110.     let interfaces = [];
  111.  
  112.     for (i in Ci) {
  113.       try {
  114.         aObject.QueryInterface(Ci[i]);
  115.         interfaces.push(i);
  116.       }
  117.       catch(ex) {}
  118.     }
  119.  
  120.     return interfaces;
  121.   },
  122.  
  123.   // Logging helper:
  124.   // let logger = Log4Moz.repository.getLogger("foo");
  125.   // logger.info(Log4Moz.enumerateProperties(someObject).join(","));
  126.   enumerateProperties: function Log4Moz_enumerateProps(aObject,
  127.                                                        aExcludeComplexTypes) {
  128.     let properties = [];
  129.  
  130.     for (p in aObject) {
  131.       try {
  132.         if (aExcludeComplexTypes &&
  133.             (typeof aObject[p] == "object" || typeof aObject[p] == "function"))
  134.           continue;
  135.         properties.push(p + " = " + aObject[p]);
  136.       }
  137.       catch(ex) {
  138.         properties.push(p + " = " + ex);
  139.       }
  140.     }
  141.  
  142.     return properties;
  143.   }
  144. };
  145.  
  146.  
  147. /*
  148.  * LogMessage
  149.  * Encapsulates a single log event's data
  150.  */
  151. function LogMessage(loggerName, level, message){
  152.   this.loggerName = loggerName;
  153.   this.message = message;
  154.   this.level = level;
  155.   this.time = Date.now();
  156. }
  157. LogMessage.prototype = {
  158.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  159.  
  160.   get levelDesc() {
  161.     if (this.level in Log4Moz.Level.Desc)
  162.       return Log4Moz.Level.Desc[this.level];
  163.     return "UNKNOWN";
  164.   },
  165.  
  166.   toString: function LogMsg_toString(){
  167.     return "LogMessage [" + this.time + " " + this.level + " " +
  168.       this.message + "]";
  169.   }
  170. };
  171.  
  172. /*
  173.  * Logger
  174.  * Hierarchical version.  Logs to all appenders, assigned or inherited
  175.  */
  176.  
  177. function Logger(name, repository) {
  178.   this._init(name, repository);
  179. }
  180. Logger.prototype = {
  181.   _init: function Logger__init(name, repository) {
  182.     if (!repository)
  183.       repository = Log4Moz.repository;
  184.     this._name = name;
  185.     this._appenders = [];
  186.     this._repository = repository;
  187.   },
  188.  
  189.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  190.  
  191.   parent: null,
  192.  
  193.   get name() {
  194.     return this._name;
  195.   },
  196.  
  197.   _level: null,
  198.   get level() {
  199.     if (this._level != null)
  200.       return this._level;
  201.     if (this.parent)
  202.       return this.parent.level;
  203.     dump("log4moz warning: root logger configuration error: no level defined\n");
  204.     return Log4Moz.Level.All;
  205.   },
  206.   set level(level) {
  207.     this._level = level;
  208.   },
  209.  
  210.   _appenders: null,
  211.   get appenders() {
  212.     if (!this.parent)
  213.       return this._appenders;
  214.     return this._appenders.concat(this.parent.appenders);
  215.   },
  216.  
  217.   addAppender: function Logger_addAppender(appender) {
  218.     for (let i = 0; i < this._appenders.length; i++) {
  219.       if (this._appenders[i] == appender)
  220.         return;
  221.     }
  222.     this._appenders.push(appender);
  223.   },
  224.  
  225.   removeAppender: function Logger_removeAppender(appender) {
  226.     let newAppenders = [];
  227.     for (let i = 0; i < this._appenders.length; i++) {
  228.       if (this._appenders[i] != appender)
  229.         newAppenders.push(this._appenders[i]);
  230.     }
  231.     this._appenders = newAppenders;
  232.   },
  233.  
  234.   log: function Logger_log(message) {
  235.     if (this.level > message.level)
  236.       return;
  237.     let appenders = this.appenders;
  238.     for (let i = 0; i < appenders.length; i++){
  239.       appenders[i].append(message);
  240.     }
  241.   },
  242.  
  243.   fatal: function Logger_fatal(string) {
  244.     this.log(new LogMessage(this._name, Log4Moz.Level.Fatal, string));
  245.   },
  246.   error: function Logger_error(string) {
  247.     this.log(new LogMessage(this._name, Log4Moz.Level.Error, string));
  248.   },
  249.   warn: function Logger_warn(string) {
  250.     this.log(new LogMessage(this._name, Log4Moz.Level.Warn, string));
  251.   },
  252.   info: function Logger_info(string) {
  253.     this.log(new LogMessage(this._name, Log4Moz.Level.Info, string));
  254.   },
  255.   config: function Logger_config(string) {
  256.     this.log(new LogMessage(this._name, Log4Moz.Level.Config, string));
  257.   },
  258.   debug: function Logger_debug(string) {
  259.     this.log(new LogMessage(this._name, Log4Moz.Level.Debug, string));
  260.   },
  261.   trace: function Logger_trace(string) {
  262.     this.log(new LogMessage(this._name, Log4Moz.Level.Trace, string));
  263.   }
  264. };
  265.  
  266. /*
  267.  * LoggerRepository
  268.  * Implements a hierarchy of Loggers
  269.  */
  270.  
  271. function LoggerRepository() {}
  272. LoggerRepository.prototype = {
  273.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  274.  
  275.   _loggers: {},
  276.  
  277.   _rootLogger: null,
  278.   get rootLogger() {
  279.     if (!this._rootLogger) {
  280.       this._rootLogger = new Logger("root", this);
  281.       this._rootLogger.level = Log4Moz.Level.All;
  282.     }
  283.     return this._rootLogger;
  284.   },
  285.   // FIXME: need to update all parent values if we do this
  286.   //set rootLogger(logger) {
  287.   //  this._rootLogger = logger;
  288.   //},
  289.  
  290.   _updateParents: function LogRep__updateParents(name) {
  291.     let pieces = name.split('.');
  292.     let cur, parent;
  293.  
  294.     // find the closest parent
  295.     // don't test for the logger name itself, as there's a chance it's already
  296.     // there in this._loggers
  297.     for (let i = 0; i < pieces.length - 1; i++) {
  298.       if (cur)
  299.         cur += '.' + pieces[i];
  300.       else
  301.         cur = pieces[i];
  302.       if (cur in this._loggers)
  303.         parent = cur;
  304.     }
  305.  
  306.     // if we didn't assign a parent above, there is no parent
  307.     if (!parent)
  308.       this._loggers[name].parent = this.rootLogger;
  309.     else
  310.       this._loggers[name].parent = this._loggers[parent];
  311.  
  312.     // trigger updates for any possible descendants of this logger
  313.     for (let logger in this._loggers) {
  314.       if (logger != name && logger.indexOf(name) == 0)
  315.         this._updateParents(logger);
  316.     }
  317.   },
  318.  
  319.   getLogger: function LogRep_getLogger(name) {
  320.     if (!name)
  321.       name = this.getLogger.caller.name;
  322.     if (name in this._loggers)
  323.       return this._loggers[name];
  324.     this._loggers[name] = new Logger(name, this);
  325.     this._updateParents(name);
  326.     return this._loggers[name];
  327.   }
  328. };
  329.  
  330. /*
  331.  * Formatters
  332.  * These massage a LogMessage into whatever output is desired
  333.  * Only the BasicFormatter is currently implemented
  334.  */
  335.  
  336. // Abstract formatter
  337. function Formatter() {}
  338. Formatter.prototype = {
  339.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  340.   format: function Formatter_format(message) {}
  341. };
  342.  
  343. // FIXME: should allow for formatting the whole string, not just the date
  344. function BasicFormatter(dateFormat) {
  345.   if (dateFormat)
  346.     this.dateFormat = dateFormat;
  347. }
  348. BasicFormatter.prototype = {
  349.   __proto__: Formatter.prototype,
  350.  
  351.   _dateFormat: null,
  352.  
  353.   get dateFormat() {
  354.     if (!this._dateFormat)
  355.       this._dateFormat = "%Y-%m-%d %H:%M:%S";
  356.     return this._dateFormat;
  357.   },
  358.  
  359.   set dateFormat(format) {
  360.     this._dateFormat = format;
  361.   },
  362.  
  363.   format: function BF_format(message) {
  364.     // Pad a string to a certain length (20) with a character (space)
  365.     let pad = function BF__pad(str, len, chr) str +
  366.       new Array(Math.max((len || 20) - str.length + 1, 0)).join(chr || " ");
  367.  
  368.     // Generate a date string because toLocaleString doesn't work XXX 514803
  369.     let z = function(n) n < 10 ? "0" + n : n;
  370.     let d = new Date(message.time);
  371.     let dateStr = [d.getFullYear(), "-", z(d.getMonth() + 1), "-",
  372.       z(d.getDate()), " ", z(d.getHours()), ":", z(d.getMinutes()), ":",
  373.       z(d.getSeconds())].join("");
  374.  
  375.     return dateStr + "\t" + pad(message.loggerName) + " " + message.levelDesc +
  376.       "\t" + message.message + "\n";
  377.   }
  378. };
  379.  
  380. /*
  381.  * Appenders
  382.  * These can be attached to Loggers to log to different places
  383.  * Simply subclass and override doAppend to implement a new one
  384.  */
  385.  
  386. function Appender(formatter) {
  387.   this._name = "Appender";
  388.   this._formatter = formatter? formatter : new BasicFormatter();
  389. }
  390. Appender.prototype = {
  391.   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
  392.  
  393.   _level: Log4Moz.Level.All,
  394.   get level() { return this._level; },
  395.   set level(level) { this._level = level; },
  396.  
  397.   append: function App_append(message) {
  398.     if(this._level <= message.level)
  399.       this.doAppend(this._formatter.format(message));
  400.   },
  401.   toString: function App_toString() {
  402.     return this._name + " [level=" + this._level +
  403.       ", formatter=" + this._formatter + "]";
  404.   },
  405.   doAppend: function App_doAppend(message) {}
  406. };
  407.  
  408. /*
  409.  * DumpAppender
  410.  * Logs to standard out
  411.  */
  412.  
  413. function DumpAppender(formatter) {
  414.   this._name = "DumpAppender";
  415.   this._formatter = formatter? formatter : new BasicFormatter();
  416. }
  417. DumpAppender.prototype = {
  418.   __proto__: Appender.prototype,
  419.  
  420.   doAppend: function DApp_doAppend(message) {
  421.     dump(message);
  422.   }
  423. };
  424.  
  425. /*
  426.  * ConsoleAppender
  427.  * Logs to the javascript console
  428.  */
  429.  
  430. function ConsoleAppender(formatter) {
  431.   this._name = "ConsoleAppender";
  432.   this._formatter = formatter;
  433. }
  434. ConsoleAppender.prototype = {
  435.   __proto__: Appender.prototype,
  436.  
  437.   doAppend: function CApp_doAppend(message) {
  438.     if (message.level > Log4Moz.Level.Warn) {
  439.       Cu.reportError(message);
  440.       return;
  441.     }
  442.     Cc["@mozilla.org/consoleservice;1"].
  443.       getService(Ci.nsIConsoleService).logStringMessage(message);
  444.   }
  445. };
  446.  
  447. /*
  448.  * FileAppender
  449.  * Logs to a file
  450.  */
  451.  
  452. function FileAppender(file, formatter) {
  453.   this._name = "FileAppender";
  454.   this._file = file; // nsIFile
  455.   this._formatter = formatter? formatter : new BasicFormatter();
  456. }
  457. FileAppender.prototype = {
  458.   __proto__: Appender.prototype,
  459.   __fos: null,
  460.   get _fos() {
  461.     if (!this.__fos)
  462.       this.openStream();
  463.     return this.__fos;
  464.   },
  465.  
  466.   openStream: function FApp_openStream() {
  467.     try {
  468.       let __fos = Cc["@mozilla.org/network/file-output-stream;1"].
  469.         createInstance(Ci.nsIFileOutputStream);
  470.       let flags = MODE_WRONLY | MODE_CREATE | MODE_APPEND;
  471.       __fos.init(this._file, flags, PERMS_FILE, 0);
  472.  
  473.       this.__fos = Cc["@mozilla.org/intl/converter-output-stream;1"]
  474.             .createInstance(Ci.nsIConverterOutputStream);
  475.       this.__fos.init(__fos, "UTF-8", 4096,
  476.             Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
  477.     } catch(e) {
  478.       dump("Error opening stream:\n" + e);
  479.     }
  480.   },
  481.  
  482.   closeStream: function FApp_closeStream() {
  483.     if (!this.__fos)
  484.       return;
  485.     try {
  486.       this.__fos.close();
  487.       this.__fos = null;
  488.     } catch(e) {
  489.       dump("Failed to close file output stream\n" + e);
  490.     }
  491.   },
  492.  
  493.   doAppend: function FApp_doAppend(message) {
  494.     if (message === null || message.length <= 0)
  495.       return;
  496.     try {
  497.       this._fos.writeString(message);
  498.     } catch(e) {
  499.       dump("Error writing file:\n" + e);
  500.     }
  501.   },
  502.  
  503.   clear: function FApp_clear() {
  504.     this.closeStream();
  505.     try {
  506.       this._file.remove(false);
  507.     } catch (e) {
  508.       // XXX do something?
  509.     }
  510.   }
  511. };
  512.  
  513. /*
  514.  * RotatingFileAppender
  515.  * Similar to FileAppender, but rotates logs when they become too large
  516.  */
  517.  
  518. function RotatingFileAppender(file, formatter, maxSize, maxBackups) {
  519.   if (maxSize === undefined)
  520.     maxSize = ONE_MEGABYTE * 2;
  521.  
  522.   if (maxBackups === undefined)
  523.     maxBackups = 0;
  524.  
  525.   this._name = "RotatingFileAppender";
  526.   this._file = file; // nsIFile
  527.   this._formatter = formatter? formatter : new BasicFormatter();
  528.   this._maxSize = maxSize;
  529.   this._maxBackups = maxBackups;
  530. }
  531. RotatingFileAppender.prototype = {
  532.   __proto__: FileAppender.prototype,
  533.  
  534.   doAppend: function RFApp_doAppend(message) {
  535.     if (message === null || message.length <= 0)
  536.       return;
  537.     try {
  538.       this.rotateLogs();
  539.       FileAppender.prototype.doAppend.call(this, message);
  540.     } catch(e) {
  541.       dump("Error writing file:" + e + "\n");
  542.     }
  543.   },
  544.  
  545.   rotateLogs: function RFApp_rotateLogs() {
  546.     if(this._file.exists() &&
  547.        this._file.fileSize < this._maxSize)
  548.       return;
  549.  
  550.     this.closeStream();
  551.  
  552.     for (let i = this.maxBackups - 1; i > 0; i--){
  553.       let backup = this._file.parent.clone();
  554.       backup.append(this._file.leafName + "." + i);
  555.       if (backup.exists())
  556.         backup.moveTo(this._file.parent, this._file.leafName + "." + (i + 1));
  557.     }
  558.  
  559.     let cur = this._file.clone();
  560.     if (cur.exists())
  561.       cur.moveTo(cur.parent, cur.leafName + ".1");
  562.  
  563.     // Note: this._file still points to the same file
  564.   }
  565. };
  566.